#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include <sys/select.h>

// 串口设备名称保持一致
const char default_path[] = "/dev/ttyS6";

// 构造开单个锁的数据帧（开0号板1号锁）
void build_open_lock_frame(unsigned char *buf, int *len)
{
    // 协议要求：57 4B 4C 59 09 00 82 01 83
    unsigned char frame[9] = {0x57, 0x4B, 0x4C, 0x59, 0x09, 0x00, 0x82, 0x01, 0x83};
    memcpy(buf, frame, 9);
    *len = 9;
}

int main(int argc, char *argv[])
{
    int fd;
    int res;
    char *path;
    unsigned char buf[32];
    int frame_len = 0;

    // 若无输入参数则使用默认终端设备
    if (argc > 1)
        path = argv[1];
    else
        path = (char *)default_path;

    // 获取串口设备描述符（阻塞模式，参照CuteCom）
    printf("This is tty/usart demo.\n");
    // 仅使用O_RDWR和O_NOCTTY，参考CuteCom
    fd = open(path, O_RDWR | O_NOCTTY);
    if (fd < 0) {
        printf("Fail to Open %s device\n", path);
        return 0;
    }

    // 串口参数设置（9600 8N1，无流控、无校验）- 参照CuteCom的参数设置方式
    struct termios opt;
    memset(&opt, 0, sizeof(opt));  // CuteCom会完全初始化结构
    tcgetattr(fd, &opt);
    
    // 输入模式标志 - 参照CuteCom
    opt.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY);
    opt.c_iflag &= ~INPCK;  // 无奇偶校验
    
    // 输出模式标志 - 参照CuteCom
    opt.c_oflag &= ~OPOST;
    
    // 控制模式标志 - 参照CuteCom
    opt.c_cflag |= (CLOCAL | CREAD);  // 忽略modem状态线，启用接收器
    opt.c_cflag &= ~CSIZE;
    opt.c_cflag |= CS8;         // 8数据位
    opt.c_cflag &= ~PARENB;     // 无校验
    opt.c_cflag &= ~CSTOPB;     // 1停止位
    opt.c_cflag &= ~CRTSCTS;    // 无硬件流控
    
    // 本地模式标志 - 参照CuteCom
    opt.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    
    // 特殊字符 - CuteCom源码关键设置
    opt.c_cc[VMIN] = 1;   // 至少读到1字节才返回
    opt.c_cc[VTIME] = 0;  // 阻塞等待
    
    // 设置波特率
    cfsetispeed(&opt, B9600);
    cfsetospeed(&opt, B9600);
    
    // 应用设置
    tcsetattr(fd, TCSANOW, &opt);
    tcflush(fd, TCIOFLUSH);  // CuteCom在设置参数后会刷新缓冲区
    printf("Device %s is set to 9600bps,8N1\n", path);

    // 构造并发送数据帧
    build_open_lock_frame(buf, &frame_len);
    write(fd, buf, frame_len);
    printf("Send frame: ");
    for(int i=0; i<frame_len; i++)
        printf("%02X ", buf[i]);
    printf("\n");
    
    // 添加select机制，模拟CuteCom的事件驱动模型
    fd_set readfds;
    struct timeval tv;
    
    // 循环接收数据
    while (1) {
        FD_ZERO(&readfds);
        FD_SET(fd, &readfds);
        
        // 设置超时时间为1秒
        tv.tv_sec = 1;
        tv.tv_usec = 0;
        
        int ret = select(fd + 1, &readfds, NULL, NULL, &tv);
        if (ret > 0) {
            res = read(fd, buf, sizeof(buf));
            if (res > 0) {
                printf("Receive res = %d bytes data: ", res);
                for(int i=0; i<res; i++)
                    printf("%02X ", buf[i]);
                printf("\n");
                break;  // 收到数据后退出
            }
        }
        else if (ret == 0) {
            printf("Waiting for response...\n");  // 超时提示
        }
        else {
            perror("select error");
            break;
        }
    }

    // 关闭前刷新缓冲区
    tcdrain(fd);  // CuteCom关闭前等待传输完成
    close(fd);
    return 0;
}
